#include <optix.h>
#include <optixu/optixu_math_namespace.h>
#include "helpers.h"
#include "path_tracer.h"
#include "materials.h"
#include "random.h"
#include "sunsky.h"
#include "materials.h"
#include "lights_sampling.h"
#include "bsdf_sampling.h"
#include "Volume.h"


using namespace optix;

rtDeclareVariable(float, scene_epsilon, , );
rtDeclareVariable(rtObject, top_object, , );
rtDeclareVariable(optix::Ray, ray, rtCurrentRay, );
rtDeclareVariable(float, t_hit, rtIntersectionDistance, );
rtDeclareVariable(unsigned int, pathtrace_ray_type, , );
rtDeclareVariable(unsigned int, pathtrace_shadow_ray_type, , );
rtDeclareVariable(unsigned int, rr_begin_depth, , );
rtDeclareVariable(unsigned int, max_depth, , );
rtDeclareVariable(unsigned int, use_envmap, , ) = 0;
rtBuffer<TriangleLight>     lights;

//geometry attributes
rtDeclareVariable(float3, geometric_normal, attribute geometric_normal, );
rtDeclareVariable(float3, shading_normal, attribute shading_normal, );
rtDeclareVariable(float3, texcoord, attribute texcoord, );
rtDeclareVariable(float3, emission_color, , );
rtDeclareVariable(float3, diffuse_color, , );
rtDeclareVariable(float3, bg_color, , );
rtDeclareVariable(int, use_texture, , ) = 0;
rtDeclareVariable(float, extinction, , ) = -0.06f;

rtDeclareVariable(float, phong_exp, , );
rtDeclareVariable(float3, directional_light, , );
rtDeclareVariable(float3, directional_light_col, , );
rtDeclareVariable(float, solid_angle, , ) = 6.0e-5f * 100.0f;

rtDeclareVariable(PerRayData_pathtrace, current_prd, rtPayload, );

//Sample Infinite Light
rtTextureSampler<float4, 2> envmap;
rtTextureSampler<float4, 2> diffuse_map;
rtTextureSampler<float4, 2> envmap_lt;

__device__ __inline__ optix::float4 rect_envmap_le(optix::float3& dir)
{
	float3 direction = -dir;
	float theta = atan2f(direction.x, direction.y);
	float phi = M_PIf * 0.5f - acosf(direction.z);
	float u = (theta + M_PIf) * (0.5f * M_1_PIf);
	float v = 0.5f * (1.0f + sin(phi));
	return make_float4(1, 1, 0, 0);
	//return tex2D(envmap_lt, u, v);
}


__device__ __inline__ void sample_lights(const optix::float3 &sample, const optix::float3 hitpoint, const optix::float3 ffnormal, optix::float3 &L, optix::float3 &Le, float &ndl, float&pdf)
{
	using namespace optix;

	const unsigned int num_lights = lights.size() - 1;
	if (num_lights <= 0) {
		return;
	}
	const float z1 = sample.x;
	const float z2 = sample.y;
	TriangleLight light = lights[sample.z*(num_lights)];
	float alpha = 1.0f - sqrt(fold(z1));
	float beta = (1.0f - fold(z2)) * sqrt(fold(z1));
	float gamma = fold(z2) * sqrt(fold(z1));
	float3 light_pos = light.v3 * gamma + light.v1 * alpha + light.v2 * beta;
	float Ldist = length(light_pos - hitpoint);
	L = normalize(light_pos - hitpoint);
	ndl = dot(ffnormal, L);
	float LnDl = dot(light.normal, L);
	float A = length(cross(light.v1 - light.v2, light.v1 - light.v3));
	if (A == 0.f) {
		pdf = 0.f;
	}
	else {
		pdf = ndl * LnDl / ((M_PIf*Ldist*Ldist) * A);
	}

	Le = (light.emission*num_lights)/pdf;

	if (LnDl < 0.f) {
		ndl = -1;
	}
}

__device__ __inline__ float transmission()
{
	//return exp(-t_hit*extinction);
	return 1.0f;
}


//-------lambert diffuse


RT_PROGRAM void diffuse_all()
{
	if (length(emission_color) > 0.0f)
	{
		current_prd.radiance = current_prd.countEmitted ? emission_color : make_float3(0.f, 0.f, 0.f);
		current_prd.done = true;
		return;
	}

	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float3 hitpoint = ray.origin + t_hit * ray.direction;
	float3 Kd = diffuse_color;
	if (use_texture) { Kd = make_float3(tex2D(diffuse_map, texcoord.x, texcoord.y)); }

	current_prd.origin = hitpoint;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);

	float z3 = powf(fold(z1), 2.0f / (1.0f + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);
	current_prd.direction = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	float3 result = make_float3(0.0f);
	current_prd.attenuation = current_prd.attenuation *  Kd;
	current_prd.countEmitted = false;

	float3 Le, L;
	float nDl = 0.f, pdf = 0.f;

	sample_lights(make_float3(rnd(current_prd.seed), rnd(current_prd.seed), rnd(current_prd.seed)), hitpoint, ffnormal, Le, L, nDl, pdf);
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);

		if (!shadow_prd.inShadow)
		{
			result += Le * pdf;
		}
	}
	current_prd.radiance = result;
}


RT_PROGRAM void diffuse_sun()
{
	if (length(emission_color) > 0.0f)
	{
		current_prd.radiance = current_prd.countEmitted ? emission_color : make_float3(1.f, 1.f, 0.f);
		current_prd.done = true;
		return;
	}
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float3 hitpoint = ray.origin + t_hit * ray.direction;
	float3 Kd = diffuse_color;
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );
	current_prd.origin = hitpoint;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);

	float z3 = powf(fold(z1), 2.0f / (1.0f + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);
	current_prd.direction = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	float3 result = make_float3(0.0f);
	current_prd.attenuation = current_prd.attenuation *  Kd;
	current_prd.countEmitted = false;
	float div = 1.0f / 1.0f;
	for (int ns = 0; ns < 1; ns++) {
		float3 L = normalize(directional_light);
		float nDl = 0.f;
		sample_sunsky(solid_angle, make_float2(rnd(current_prd.seed), rnd(current_prd.seed)), ffnormal, L, nDl);

		// cast shadow ray
		if (nDl > 0.0f)
		{
			PerRayData_pathtrace_shadow shadow_prd;
			shadow_prd.inShadow = false;
			Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
			rtTrace(top_object, shadow_ray, shadow_prd);

			if (!shadow_prd.inShadow)
			{
				float weight = nDl;
				result += directional_light_col * weight;
			}
		}
	}
	//}
	//result*=make_float3(0.25f,0.25f,0.25f);
	current_prd.radiance = result;
}

RT_PROGRAM void diffuse_tex_sun()
{
	if (length(emission_color) > 0.0f)
	{
		current_prd.radiance = current_prd.countEmitted ? emission_color : make_float3(1.f, 1.f, 0.f);
		current_prd.done = true;
		return;
	}
	float2 uv = make_float2(texcoord);
	float3 Kd = make_float3(tex2D(diffuse_map, uv.x, uv.y));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float3 hitpoint = ray.origin + t_hit * ray.direction;

	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );
	current_prd.origin = hitpoint;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);

	float z3 = powf(fold(z1), 2.0f / (1.0f + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);
	current_prd.direction = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	float3 result = make_float3(0.0f);
	current_prd.attenuation = current_prd.attenuation *  Kd;
	current_prd.countEmitted = false;
	float div = 1.0f;
	for (int ns = 0; ns < 1; ns++) {
		float3 L = normalize(directional_light);
		float nDl = 0.f;
		sample_sunsky(solid_angle, make_float2(rnd(current_prd.seed), rnd(current_prd.seed)), ffnormal, L, nDl);

		// cast shadow ray
		if (nDl > 0.0f)
		{
			PerRayData_pathtrace_shadow shadow_prd;
			shadow_prd.inShadow = false;
			Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
			rtTrace(top_object, shadow_ray, shadow_prd);

			if (!shadow_prd.inShadow || (dot(shadow_prd.attenuation, shadow_prd.attenuation) > 0.0))
			{
				float3 weight = directional_light_col*shadow_prd.attenuation;
				result += weight* nDl;
			}
		}
	}
	current_prd.radiance = result;
}

RT_PROGRAM void diffuse_envmap()
{
	if (length(emission_color) > 0.0f)
	{
		current_prd.radiance = current_prd.countEmitted ? emission_color : make_float3(1.f, 1.f, 0.f);
		current_prd.done = true;
		return;
	}
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float3 hitpoint = ray.origin + t_hit * ray.direction;
	float3 Kd = diffuse_color;
	current_prd.origin = hitpoint;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);

	float z3 = powf(fold(z1), 2.0f / (1.0f + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);
	current_prd.direction = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	float3 result = make_float3(0.0f);
	current_prd.attenuation = current_prd.attenuation *  Kd;
	current_prd.countEmitted = false;
	float3 L;


	{
		float z4 = rnd(current_prd.seed);
		float z5 = rnd(current_prd.seed);
		float z6 = powf(fold(z4), 2.0f / (1.0f + 1.0f));
		float sintheta = sqrtf(1.0f - z6);
		float costheta = sqrtf(z6);
		float phi = (2.0f * M_PIf) * z5;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v3, v4;
		create_onb(ffnormal, v3, v4);
		L = (ffnormal)*costheta + v3 * cosphi + v4 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		shadow_prd.attenuation = make_float3(1.0f);
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);
		float3 att = make_float3(rect_envmap_le(L)) * shadow_prd.attenuation;


		//if (!shadow_prd.inShadow)
		{
			float weight = nDl;
			result += att* weight*shadow_prd.attenuation;
		}
	}
	current_prd.radiance = result;
}

RT_PROGRAM void diffuse_tex_envmap()
{
	if (length(emission_color) > 0.0f)
	{
		current_prd.radiance = current_prd.countEmitted ? emission_color : make_float3(1.f, 1.f, 0.f);
		current_prd.done = true;
		return;
	}
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float3 hitpoint = ray.origin + t_hit * ray.direction;
	float2 uv = make_float2(texcoord);

	float3 Kd = make_float3(tex2D(diffuse_map, uv.x, uv.y));
	current_prd.origin = hitpoint;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);

	float z3 = powf(fold(z1), 2.0f / (1.0f + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);
	current_prd.direction = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	float3 result = make_float3(0.0f);
	current_prd.attenuation = current_prd.attenuation *  Kd;
	current_prd.countEmitted = false;
	float3 L;


	{
		float z4 = rnd(current_prd.seed);
		float z5 = rnd(current_prd.seed);
		float z6 = powf(fold(z4), 2.0f / (1.0f + 1.0f));
		float sintheta = sqrtf(1.0f - z6);
		float costheta = sqrtf(z6);
		float phi = (2.0f * M_PIf) * z5;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v3, v4;
		create_onb(-ffnormal, v3, v4);
		L = (-ffnormal)*costheta + v3 * cosphi + v4 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		shadow_prd.attenuation = make_float3(1.0);
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);

		rtTrace(top_object, shadow_ray, shadow_prd);
		float3 att = make_float3(rect_envmap_le(L)) * shadow_prd.attenuation;


		if (!shadow_prd.inShadow)
		{
			float weight = nDl;
			result += att * weight*Kd*shadow_prd.attenuation;
		}
	}
	current_prd.radiance = Kd;
}

RT_PROGRAM void diffuse_trilight()
{
	if (length(emission_color) > 0.0f)
	{
		current_prd.radiance = current_prd.countEmitted ? emission_color : make_float3(1.f, 1.f, 0.f);
		current_prd.done = true;
		return;
	}
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float3 hitpoint = ray.origin + t_hit * ray.direction;
	float3 Kd = diffuse_color;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);

	float z3 = powf(fold(z1), 2.0f / (1.0f + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);
	current_prd.direction = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	float3 result = make_float3(0.0f);
	current_prd.attenuation = current_prd.attenuation *  Kd;
	current_prd.countEmitted = false;
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );
	current_prd.origin = hitpoint;
	unsigned int num_lights = lights.size();
	//for(int i=0;i<num_lights;i++)
	{
		TriangleLight light = lights[rnd(current_prd.seed)*(num_lights)];
		//TriangleLight light = lights[i];

		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);


		float alpha = 1.0f - sqrt(fold(z1));
		float beta = (1.0f - fold(z2)) * sqrt(fold(z1));
		float gamma = fold(z2) * sqrt(fold(z1));
		float3 light_pos = light.v3 * gamma + light.v1 * alpha + light.v2 * beta;

		float Ldist = length(light_pos - hitpoint);
		float3 L = normalize(light_pos - hitpoint);
		float nDl = dot(ffnormal, L);
		float LnDl = dot(light.normal, L);
		float A = length(cross(light.v2 - light.v3, light.v1 - light.v3));

		// cast shadow ray
		if (nDl > 0.0f && LnDl > 0.0f)
		{
			PerRayData_pathtrace_shadow shadow_prd;
			shadow_prd.inShadow = false;
			shadow_prd.attenuation = make_float3(1.0f);
			Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, Ldist);
			rtTrace(top_object, shadow_ray, shadow_prd);
			float3 attenuation = shadow_prd.attenuation;
			float3 lAttenuation = light.emission;


			if (!shadow_prd.inShadow)
			{
				float weight = nDl * LnDl / (M_PIf*Ldist*Ldist) * A;
				if (dot(attenuation, attenuation) > 0.0)
					lAttenuation *= attenuation;
				result += num_lights*transmission()*lAttenuation * weight*shadow_prd.attenuation;

			}
		}
	}
	current_prd.radiance = result;
}

RT_PROGRAM void diffuse_tex_trilight()
{
	if (length(emission_color) > 0.0f)
	{
		current_prd.radiance = current_prd.countEmitted ? emission_color : make_float3(1.f, 1.f, 0.f);
		current_prd.done = true;
		return;
	}
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float2 uv = make_float2(texcoord);

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float3 hitpoint = ray.origin + t_hit * ray.direction;
	float3 Kd = make_float3(tex2D(diffuse_map, uv.x, uv.y));
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );
	current_prd.origin = hitpoint;


	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);

	float z3 = powf(fold(z1), 2.0f / (1.0f + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);
	current_prd.direction = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	float3 result = Kd + make_float3(0.0f);
	current_prd.attenuation = current_prd.attenuation *  Kd;
	current_prd.countEmitted = false;

	unsigned int num_lights = lights.size();
	{
		TriangleLight light = lights[rnd(current_prd.seed)*(num_lights)];
		//TriangleLight light = lights[i];

		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);


		float alpha = 1.0f - sqrt(fold(z1));
		float beta = (1.0f - fold(z2)) * sqrt(fold(z1));
		float gamma = fold(z2) * sqrt(fold(z1));
		float3 light_pos = light.v3 * gamma + light.v1 * alpha + light.v2 * beta;

		float Ldist = length(light_pos - hitpoint);
		float3 L = normalize(light_pos - hitpoint);
		float nDl = dot(ffnormal, L);
		float LnDl = dot(light.normal, L);
		float A = length(cross(light.v2 - light.v3, light.v1 - light.v3));

		// cast shadow ray
		if (nDl > 0.0f && LnDl > 0.0f)
		{
			PerRayData_pathtrace_shadow shadow_prd;
			shadow_prd.inShadow = false;
			shadow_prd.attenuation = make_float3(1.0, 1.0, 1.0);
			Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, Ldist);
			rtTrace(top_object, shadow_ray, shadow_prd);
			float3 attenuation = shadow_prd.attenuation;
			float3 lAttenuation = light.emission;

			if (!shadow_prd.inShadow)
			{
				float weight = nDl * LnDl / (M_PIf*Ldist*Ldist) * A;
				result += num_lights*transmission()*lAttenuation * weight*shadow_prd.attenuation;

			}
		}
	}
	current_prd.radiance = result;
}


// ---- glossy metal
RT_PROGRAM void glossymetal_envmap()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;

	float3 Kd = diffuse_color;
	current_prd.origin = hitpoint;
	current_prd.countEmitted = true;
	current_prd.radiance = make_float3(0.0f);

	// specular reflection
	current_prd.direction = reflect(ray.direction, world_shading_normal);
	current_prd.attenuation = current_prd.attenuation * Kd;
}
//-----end of glossy metal



//--------metal
RT_PROGRAM void metal_envmap()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;

	float3 Kd = diffuse_color;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);
	float z3 = powf(fold(z1), 2.0f / (phong_exp + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);

	float3 h = ffnormal * costheta + v1 * cosphi + v2 * sinphi;

	current_prd.direction = reflect(ray.direction, h);
	if (dot(current_prd.direction, ffnormal) < 0.0) current_prd.direction = current_prd.direction;

	current_prd.attenuation = current_prd.attenuation * Kd;
	current_prd.countEmitted = false;

	// --------------------------------
	// compute direct light
	// --------------------------------
	float3 result = make_float3(0.0f);
	float3 L;

	// perturb the direction based on given solid angle
	{
		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);

		float cosmax = 1.0f - M_PIf / (2.0f * M_PIf);
		float z3 = 1.0f - fold(z2) * (1.0f - cosmax);

		float sintheta = sqrtf(1.0f - z3 * z3);
		float costheta = z3 * z3;
		float phi = (2.0f * M_PIf) * z1;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v1, v2;
		create_onb(-ffnormal, v1, v2);
		L = -ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		shadow_prd.attenuation = make_float3(1.0f);
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);

		if (!shadow_prd.inShadow)
		{
			float3 h = normalize(-ray.direction + shadow_ray.direction);
			float nDotH = dot(ffnormal, h);
			float weight = powf(max(nDotH, 0.0f), phong_exp) * (phong_exp + 2.0f) / (M_PIf * 2.0f);
			result += make_float3(transmission()*rect_envmap_le(ray.direction)) * weight*shadow_prd.attenuation;
		}
	}
	current_prd.radiance = result;
}

RT_PROGRAM void metal_tex_envmap()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);
	float2 uv = make_float2(texcoord);
	float3 hitpoint = ray.origin + t_hit * ray.direction;

	float3 Kd = make_float3(tex2D(diffuse_map, uv.x, uv.y));

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);
	float z3 = powf(fold(z1), 2.0f / (phong_exp + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);

	float3 h = ffnormal * costheta + v1 * cosphi + v2 * sinphi;

	current_prd.direction = reflect(ray.direction, h);
	if (dot(current_prd.direction, ffnormal) < 0.0) current_prd.direction = current_prd.direction;

	current_prd.attenuation = current_prd.attenuation * Kd;
	current_prd.countEmitted = false;

	// --------------------------------
	// compute direct light
	// --------------------------------
	float3 result = make_float3(0.0f);
	float3 L;

	// perturb the direction based on given solid angle
	{
		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);

		float cosmax = 1.0f - M_PIf / (2.0f * M_PIf);
		float z3 = 1.0f - fold(z2) * (1.0f - cosmax);

		float sintheta = sqrtf(1.0f - z3 * z3);
		float costheta = z3 * z3;
		float phi = (2.0f * M_PIf) * z1;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v1, v2;
		create_onb(-ffnormal, v1, v2);
		L = -ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		shadow_prd.attenuation = make_float3(1.0f);
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);

		if (!shadow_prd.inShadow)
		{
			float3 h = normalize(-ray.direction + shadow_ray.direction);
			float nDotH = dot(ffnormal, h);
			float weight = powf(max(nDotH, 0.0f), phong_exp) * (phong_exp + 2.0f) / (M_PIf * 2.0f);
			result += make_float3(transmission()*rect_envmap_le(ray.direction)) * weight*shadow_prd.attenuation;
		}
	}
	current_prd.radiance = result;
}
////!!!!!!!!!!!!!
RT_PROGRAM void metal_sun()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;

	float3 Kd = diffuse_color;
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);
	float z3 = powf(fold(z1), 2.0f / (phong_exp + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);

	float3 h = ffnormal * costheta + v1 * cosphi + v2 * sinphi;

	current_prd.direction = reflect(ray.direction, h);
	if (dot(current_prd.direction, ffnormal) < 0.0) current_prd.direction = current_prd.direction;

	current_prd.attenuation = current_prd.attenuation * Kd;
	current_prd.countEmitted = false;

	// --------------------------------
	// compute direct light
	// --------------------------------
	float3 result = make_float3(0.0f);
	float3 L = normalize(directional_light);

	// perturb the direction based on given solid angle
	{
		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);
		float cosmax = 1.0f - solid_angle / (2.0f * M_PIf);
		float z3 = 1.0f - fold(z2) * (1.0f - cosmax);

		float sintheta = sqrtf(1.0f - z3 * z3);
		float costheta = z3 * z3;
		float phi = (2.0f * M_PIf) * z1;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v1, v2;
		create_onb(L, v1, v2);
		L = L * costheta + v1 * cosphi + v2 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);

		if (!shadow_prd.inShadow)
		{
			float3 h = normalize(-ray.direction + shadow_ray.direction);
			float nDotH = dot(ffnormal, h);
			float weight = powf(max(nDotH, 0.0f), phong_exp) * (phong_exp + 2.0f) / (M_PIf * 2.0f);

			result += transmission()*directional_light_col * weight;
		}
	}
	current_prd.radiance = result;
}


RT_PROGRAM void metal_trilight()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;

	float3 Kd = diffuse_color;
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);
	float z3 = powf(fold(z1), 2.0f / (phong_exp + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);

	float3 h = ffnormal * costheta + v1 * cosphi + v2 * sinphi;

	current_prd.direction = reflect(ray.direction, h);
	if (dot(current_prd.direction, ffnormal) < 0.0) current_prd.direction = current_prd.direction;

	current_prd.attenuation = current_prd.attenuation * Kd;
	current_prd.countEmitted = false;

	// --------------------------------
	// compute direct light
	// --------------------------------
	unsigned int num_lights = lights.size();
	float3 result = make_float3(0.f);
	{
		TriangleLight light = lights[rnd(current_prd.seed)*(num_lights)];
		//TriangleLight light = lights[i];

		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);


		float alpha = 1.0f - sqrt(fold(z1));
		float beta = (1.0f - fold(z2)) * sqrt(fold(z1));
		float gamma = fold(z2) * sqrt(fold(z1));
		float3 light_pos = light.v3 * gamma + light.v1 * alpha + light.v2 * beta;

		float Ldist = length(light_pos - hitpoint);
		float3 L = normalize(light_pos - hitpoint);
		float nDl = dot(ffnormal, L);
		float LnDl = dot(light.normal, L);
		float A = length(cross(light.v2 - light.v3, light.v1 - light.v3));

		// cast shadow ray
		if (nDl > 0.0f && LnDl > 0.0f)
		{
			PerRayData_pathtrace_shadow shadow_prd;
			shadow_prd.inShadow = false;
			Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, Ldist);
			rtTrace(top_object, shadow_ray, shadow_prd);

			if (!shadow_prd.inShadow)
			{
				float3 h = normalize(-ray.direction + shadow_ray.direction);
				float nDotH = dot(ffnormal, h);
				float weight = LnDl / (M_PIf*Ldist*Ldist) * A * powf(max(nDotH, 0.0f), phong_exp) * (phong_exp + 2.0f) / (M_PIf * 2.0f);

				result += num_lights * light.emission * weight;
			}

		}
	}
	current_prd.radiance = result;
}
///!!!!
RT_PROGRAM void envmap_miss()
{
	current_prd.done = true;

	float3 direction = -ray.direction;
	float theta = atan2f(direction.x, direction.y);
	float phi = M_PIf * 0.5f - acosf(direction.z);
	float u = (theta + M_PIf) * (0.5f * M_1_PIf);
	float v = 0.5f * (1.0f + sin(phi));

	current_prd.radiance = current_prd.countEmitted ? make_float3(tex2D(envmap_lt, u, v)) : make_float3(0.0f);
	/*
	  float3 L = normalize(directional_light);
	  float nDl = dot( ray.direction, L );
	  if ( nDl > (1.0f - solid_angle / (2.0f * M_PIf)) )
	  {
	  // avoid double counting illumination
	  current_prd.radiance = current_prd.countEmitted ? make_float3( tex2D(envmap, u, v) ) * (M_PIf / solid_angle) : make_float3(0.0f);
	  }*/
}
///Alloy Material
//--------metal
RT_PROGRAM void alloy_envmap()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;

	float3 Kd = diffuse_color;

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);
	float z3 = powf(fold(z1), 2.0f / (phong_exp + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);

	float3 h = ffnormal * costheta + v1 * cosphi + v2 * sinphi;

	current_prd.direction = reflect(ray.direction, h);
	if (dot(current_prd.direction, ffnormal) < 0.0) current_prd.direction = current_prd.direction;

	current_prd.attenuation = current_prd.attenuation * Kd;
	current_prd.countEmitted = false;

	// --------------------------------
	// compute direct light
	// --------------------------------
	float3 result = make_float3(0.0f);
	float3 L;

	// perturb the direction based on given solid angle
	{
		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);

		float cosmax = 1.0f - M_PIf / (2.0f * M_PIf);
		float z3 = 1.0f - fold(z2) * (1.0f - cosmax);

		float sintheta = sqrtf(1.0f - z3 * z3);
		float costheta = z3 * z3;
		float phi = (2.0f * M_PIf) * z1;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v1, v2;
		create_onb(ffnormal, v1, v2);
		L = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);

		if (!shadow_prd.inShadow)
		{
			float3 h = normalize(-ray.direction + shadow_ray.direction);
			float nDotH = dot(ffnormal, h);
			float weight = powf(max(nDotH, 0.0f), phong_exp) * (phong_exp + 2.0f) / (M_PIf * 2.0f);
			result += make_float3(rect_envmap_le(ray.direction)) * weight;
		}
	}
	current_prd.radiance = result;
}
RT_PROGRAM void alloy_envmap_tex()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;
	float2 uv = make_float2(texcoord);

	float3 Kd = make_float3(tex2D(diffuse_map, uv.x, uv.y));

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);
	float z3 = powf(fold(z1), 2.0f / (phong_exp + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);

	float3 h = ffnormal * costheta + v1 * cosphi + v2 * sinphi;

	current_prd.direction = reflect(ray.direction, h);
	if (dot(current_prd.direction, ffnormal) < 0.0) current_prd.direction = current_prd.direction;

	current_prd.attenuation = current_prd.attenuation * Kd;
	current_prd.countEmitted = false;

	// --------------------------------
	// compute direct light
	// --------------------------------
	float3 result = make_float3(0.0f);
	float3 L;

	// perturb the direction based on given solid angle
	{
		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);

		float cosmax = 1.0f - M_PIf / (2.0f * M_PIf);
		float z3 = 1.0f - fold(z2) * (1.0f - cosmax);

		float sintheta = sqrtf(1.0f - z3 * z3);
		float costheta = z3 * z3;
		float phi = (2.0f * M_PIf) * z1;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v1, v2;
		create_onb(ffnormal, v1, v2);
		L = ffnormal * costheta + v1 * cosphi + v2 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);

		if (!shadow_prd.inShadow)
		{
			float3 h = normalize(-ray.direction + shadow_ray.direction);
			float nDotH = dot(ffnormal, h);
			float weight = powf(max(nDotH, 0.0f), phong_exp) * (phong_exp + 2.0f) / (M_PIf * 2.0f);
			result += make_float3(rect_envmap_le(ray.direction)) * weight;
		}
	}
	current_prd.radiance = result;
}
RT_PROGRAM void alloy_sun()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;

	float3 Kd = diffuse_color;
	//make_float3( tex2D(diffuse_map,  texcoord.x, texcoord.y) );

	float z1 = rnd(current_prd.seed);
	float z2 = rnd(current_prd.seed);
	float z3 = powf(fold(z1), 2.0f / (phong_exp + 1.0f));
	float sintheta = sqrtf(1.0f - z3);
	float costheta = sqrtf(z3);
	float phi = (2.0f * M_PIf) * z2;
	float cosphi = cosf(phi) * sintheta;
	float sinphi = sinf(phi) * sintheta;
	float3 v1, v2;
	create_onb(ffnormal, v1, v2);

	float3 h = ffnormal * costheta + v1 * cosphi + v2 * sinphi;

	current_prd.direction = reflect(ray.direction, h);
	if (dot(current_prd.direction, ffnormal) < 0.0) current_prd.direction = current_prd.direction;

	current_prd.attenuation = current_prd.attenuation * Kd;
	current_prd.countEmitted = false;

	// --------------------------------
	// compute direct light
	// --------------------------------
	float3 result = make_float3(0.0f);
	float3 L = normalize(directional_light);

	// perturb the direction based on given solid angle
	{
		float z1 = rnd(current_prd.seed);
		float z2 = rnd(current_prd.seed);
		float cosmax = 1.0f - solid_angle / (2.0f * M_PIf);
		float z3 = 1.0f - fold(z2) * (1.0f - cosmax);

		float sintheta = sqrtf(1.0f - z3 * z3);
		float costheta = z3 * z3;
		float phi = (2.0f * M_PIf) * z1;
		float cosphi = cosf(phi) * sintheta;
		float sinphi = sinf(phi) * sintheta;
		float3 v1, v2;
		create_onb(L, v1, v2);
		L = L * costheta + v1 * cosphi + v2 * sinphi;
	}

	float nDl = dot(ffnormal, L);

	// cast shadow ray
	if (nDl > 0.0f)
	{
		PerRayData_pathtrace_shadow shadow_prd;
		shadow_prd.inShadow = false;
		Ray shadow_ray = make_Ray(hitpoint, L, pathtrace_shadow_ray_type, scene_epsilon, RT_DEFAULT_MAX);
		rtTrace(top_object, shadow_ray, shadow_prd);

		if (!shadow_prd.inShadow)
		{
			float3 h = normalize(-ray.direction + shadow_ray.direction);
			float nDotH = dot(ffnormal, h);
			float weight = powf(max(nDotH, 0.0f), phong_exp) * (phong_exp + 2.0f) / (M_PIf * 2.0f);

			result += directional_light_col * weight;
		}
	}
	current_prd.radiance = result;
}



//End Alloy

rtDeclareVariable(float3, glass_color, , );
rtDeclareVariable(float, index_of_refraction, , );

RT_PROGRAM void glass()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;

	current_prd.origin = hitpoint;
	current_prd.countEmitted = true;
	current_prd.radiance = make_float3(0.0f);

	const float n1 = 1.0f;
	const float n2 = index_of_refraction;

	float3 reflect_dir = reflect(ray.direction, world_shading_normal);
	bool into = (dot(ray.direction, world_shading_normal) < 0.0f);
	float n_ratio = into ? (n1 / n2) : (n2 / n1);
	float dDn = dot(ray.direction, ffnormal);
	float cos2t = 1.0f - n_ratio * n_ratio * (1.0f - dDn * dDn);
	float3 Kd = diffuse_color;

	if (cos2t < 0.0f)
	{
		// total internal reflection
		current_prd.direction = reflect_dir;
	}
	else
	{
		float3 refract_dir = normalize(ray.direction * n_ratio - ffnormal * (dDn * n_ratio + sqrtf(cos2t)));
		float cost = dot(refract_dir, world_shading_normal);

		// Fresnel reflectance
		float Rs = (n_ratio * abs(dDn) - abs(cost)) / (n_ratio * abs(dDn) + abs(cost));
		float Rp = (n_ratio * abs(cost) - abs(dDn)) / (n_ratio * abs(cost) + abs(dDn));
		float Re = (Rs * Rs + Rp * Rp) * 0.5f;

		// Russian roulette
		float z1 = rnd_stratified(current_prd.seed);
		if (fold(z1) < Re)
		{
			// reflection
			current_prd.direction = reflect_dir;
		}
		else
		{
			// refraction
			current_prd.attenuation = current_prd.attenuation * Kd;
			current_prd.direction = refract_dir;
		}
	}
}

RT_PROGRAM void glass_refract()
{
	float3 world_shading_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	float3 ffnormal = faceforward(world_shading_normal, -ray.direction, world_geometric_normal);

	float3 hitpoint = ray.origin + t_hit * ray.direction;
	current_prd.origin = hitpoint;
	current_prd.countEmitted = true;
	float iof;
	if (current_prd.inside) {
		// Shoot outgoing ray
		iof = 1.0f / index_of_refraction;
	}
	else {
		iof = index_of_refraction;
	}
	refract(current_prd.direction, ray.direction, ffnormal, iof);
	//prd.direction = reflect(ray.direction, ffnormal);

	if (current_prd.inside) {
		// Compute Beer's law
		current_prd.attenuation = current_prd.attenuation * powf(glass_color, t_hit);
	}
	current_prd.inside = !current_prd.inside;

	current_prd.radiance = make_float3(0.0f);
}



rtDeclareVariable(PerRayData_pathtrace_shadow, current_prd_shadow, rtPayload, );
rtDeclareVariable(float3, shadow_attenuation, , );
rtDeclareVariable(float, importance_cutoff, , ) = 0.0025f;

RT_PROGRAM void glass_shadow()
{
	float3 world_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, shading_normal));
	float nDi = fabs(dot(world_normal, ray.direction));
	current_prd_shadow.inShadow = false;
	current_prd_shadow.attenuation *= 1 - fresnel_schlick(nDi, 5, 1 - shadow_attenuation, make_float3(1));
	if (optix::luminance(current_prd_shadow.attenuation) < importance_cutoff) {
		rtTerminateRay();
	}
	else {
		rtIgnoreIntersection();
	}
}

RT_PROGRAM void diffuseEmitterShadow()
{
	current_prd_shadow.attenuation += transmission()*make_float3(1, 1, 1);
	current_prd_shadow.inShadow = false;
}


RT_PROGRAM void diffuseEmitterCL()
{
	float3 world_geometric_normal = normalize(rtTransformNormal(RT_OBJECT_TO_WORLD, geometric_normal));

	if (dot(world_geometric_normal, ray.direction) > 0.0f)
		current_prd.radiance = make_float3(0.0f);
	else
		current_prd.radiance = diffuse_color + (transmission()*current_prd.countEmitted ? emission_color : make_float3(0.f));
	current_prd.done = true;
}
RT_PROGRAM void hg_shadow()
{
	current_prd_shadow.inShadow = false;
	current_prd_shadow.distance = t_hit;
	rtIgnoreIntersection();
}

RT_PROGRAM void shadow()
{
	if (fmaxf(emission_color) == 0.0f)
	{
		current_prd_shadow.inShadow = true;
		current_prd_shadow.attenuation = make_float3(0.0f);
		rtTerminateRay();
	}
}
//-----------------------------------------------------------------------------
//
//  Miss program
//
//-----------------------------------------------------------------------------



RT_PROGRAM void miss()
{
	current_prd.radiance = bg_color;
	current_prd.done = true;
}
